package com.github.baichuan.web.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.baichuan.web.audit.Audit;
import com.github.baichuan.web.audit.AuditContext;
import com.github.baichuan.web.audit.AuditLog;
import com.github.baichuan.web.data.TimeFormat;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 用户操作审计拦截器
 *
 */
@Slf4j
public class AuditInterceptor implements HandlerInterceptor {
    private AuditContext context;

    private ObjectMapper jsonMapper = new ObjectMapper();

    public AuditInterceptor(AuditContext context){
        this.context = context;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(!(handler instanceof HandlerMethod)){
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Audit audit = method.getAnnotation(Audit.class);
        if(audit == null)
            return true;

        long startTime = System.currentTimeMillis();
        request.setAttribute("audit:startTime", startTime);
        request.setAttribute("audit:annotation", audit);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        Object annotation = request.getAttribute("audit:annotation");
        if(annotation == null){
            return;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        Audit audit = (Audit) annotation;
        Long startTime = (Long) request.getAttribute("audit:startTime");
        long executeTime = System.currentTimeMillis() - startTime;

        AuditLog log = new AuditLog();
        log.setApplication(context.application());
        log.setOperationTime(LocalDateTime.now().format(TimeFormat.LOCAL_DATE_TIME_MS));
        log.setOperator(context.operator(request));
        log.setName(audit.name());
        log.setCode(audit.code());
        log.setPath(request.getPathInfo());
        log.setIp(ClientIP.get(request));
        log.setExecuteTime((int)executeTime);

        ServletInputStream inputStream = request.getInputStream();
        inputStream.mark(request.getContentLength());
        String body = new String(inputStream.readAllBytes(), request.getCharacterEncoding());
        inputStream.reset();
        log.setRequest(body);

        log.setStatusCode(response.getStatus());
        log.setResponseMine(response.getContentType());
        log.setResponse(jsonMapper.writeValueAsString(modelAndView.getModel()));

        AuditInterceptor.log.info(log.toString());
    }
}
